package util;

import org.sqlite.SQLiteConfig;
import org.sqlite.SQLiteDataSource;

import javax.sql.DataSource;
import java.io.File;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

//数据库连接工具类
public class DBUtil {
    /*
    1，获取数据源，设置账号密码，连接地址
    2，获取数据库的连接，Statement对象执行sql语句
    3，执行sql语句，
    4，处理查询结果集，
    5，释放资源
     */
    //使用单例恶汉模式创建数据源对象。
    private static volatile DataSource DATASOURCE;

    private volatile static Connection CONN;
    /**
     * 将获取数据源的方法私有化，保证只有一个数据源对象，确保单例模式
     * @return 然会创建后的数据源对象
     */
    private static DataSource getDATASOURCE(){
        //使用double-check双重加锁，检测数据源
        if (DATASOURCE == null){//如果多个线程同步访问数据源，再次判断，如果为空，就在创建数据源
            synchronized (DataSource.class){//在次进行加锁，保证多线程的安全问题，确保数据源的唯一性
                if (DATASOURCE == null){//当一个线程创建完成以后，下一个线程进入，再次判断，避免多次创建数据源
                    SQLiteConfig config = new SQLiteConfig();//获取sql的配置对象，
                    config.setDateStringFormat(Util.DATE_PATTER);//通过配置对象，配置日期格式
                    DATASOURCE = new SQLiteDataSource();
                    //配置文件路径数sqlite子类特有的方法，因此，需要将dataSource向下转型
                    ((SQLiteDataSource)DATASOURCE).setUrl(getUrl());//配置数据库的地址
                }
            }
        }
        return  DATASOURCE;
    }

    private static String getUrl(){
        String path = "D:\\javaCode\\myEverything\\target";//一般将嵌入式的数据库都装在target目录下，
        //sqlite数据库没有客户端和服务器端，因此不需要配置地址和端口号，只有需要配置协议+数据库路径
        String url = "jdbc:sqlite://" + path + File.separator + "search_everything.db";
        System.out.println("获取数据库的地址为：" + url);
        return url;
    }

    /**
     * 多线程情境下，通过此方法，返回一个全局唯一的数据连接对象
     * @return
     * @throws SQLException
     */
    public static Connection getConnection() throws SQLException {
        if (CONN == null){
            synchronized (DBUtil.class){
                if (CONN == null){
                    CONN = getDATASOURCE().getConnection();
                }
            }
        }
        return CONN;
    }

    /**
     * 关闭资源,因为数据库连接对象全局唯一，所以在一个线程访问结束以后不能立即关闭connenct，那样子会导致其他线程无法获取连接
     * @param statement
     */
    public static void close(Statement statement){
        if (statement != null){
            try {
                statement.close();
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        }
    }
    public static void close(Statement statement, ResultSet resultSet){

        if (resultSet != null){
            try {
                resultSet.close();
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        }
        if (statement != null){
            try {
                statement.close();
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        }
    }

    public static void main(String[] args) throws SQLException {
        System.out.println(getConnection());//最终只向外界提供一个获取数据库连接对象的方法。封装其他方法
    }
}
